package cn.wind.xboot.framework.filter;

import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.google.common.collect.Lists;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.Ordered;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.web.filter.OncePerRequestFilter;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.util.List;
import java.util.concurrent.atomic.AtomicLong;

/**
 * <p>Title: RequestFilter</p>
 * <p>Description: TODO</p>
 *
 * @author xukk
 * @version 1.0
 * @date 2018/8/14
 */
@Slf4j
public class RequestFilter extends OncePerRequestFilter   implements Ordered {
    private int order = 1;
    private List<MediaType> mediaTypes = Lists.newArrayList(MediaType.APPLICATION_JSON);
    private AtomicLong id = new AtomicLong(1);

    @Override
    public void destroy() {

    }

    public RequestFilter() {
    }

    public RequestFilter(List<MediaType> mediaTypes) {
        this.mediaTypes = mediaTypes;
    }

    @Override
    protected void doFilterInternal(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, FilterChain filterChain) throws ServletException, IOException {
        Boolean flag=checkFilter(httpServletRequest);
        if(log.isInfoEnabled()&&flag){
            long requestId = id.incrementAndGet();
            httpServletRequest = new RequestWrapper(requestId, httpServletRequest);
            httpServletResponse = new ResponseWrapper(requestId,httpServletResponse);
            logRequest(httpServletRequest);
        }
        try {
            filterChain.doFilter(httpServletRequest,httpServletResponse);
        } finally {
            if (log.isInfoEnabled()
                    &&flag
                    &&!HttpMethod.valueOf(httpServletRequest.getMethod()).equals(HttpMethod.GET)) {
                logResponse((ResponseWrapper) httpServletResponse);
            }
        }

    }

    /**
     * 判断请求/返回是否为application/json
     * 是则进行拦截，
     * 否则退出
     *
     * @param contentType 请求/响应类型
     */
    private boolean checkFilter(HttpServletRequest request) {
        if(HttpMethod.valueOf(request.getMethod()).equals(HttpMethod.GET)){
            return true;
        }
        if(request.getContentType()!=null){
            MediaType mediaType = MediaType.parseMediaType(request.getContentType());
            for (int i = 0; i <this.mediaTypes.size() ; i++) {
                if(this.mediaTypes.get(i).includes(mediaType)){
                    return true;
                }
            }
        }
        return false;
    }


    private void logRequest(final HttpServletRequest request) {
        JSONObject jsonObject=new JSONObject();
       jsonObject.put("uri",request.getRequestURI());
        if (request.getQueryString() != null) {
           jsonObject.put("queryString",request.getQueryString());
        }
        if (request instanceof RequestWrapper) {
          jsonObject.put("request id",((RequestWrapper) request).getId());
        }
        HttpSession session = request.getSession(false);
        if (session != null) {
           jsonObject.put("session id",session.getId());
        }
        if (request.getMethod() != null) {
           jsonObject.put("method",request.getMethod());
        }
        if (request.getContentType() != null) {
            jsonObject.put("content type",request.getContentType());
        }
        if (request instanceof RequestWrapper && !isMultipart(request) && !isBinaryContent(request)) {
            RequestWrapper requestWrapper = (RequestWrapper) request;
            try {
                String charEncoding = requestWrapper.getCharacterEncoding() != null ? requestWrapper.getCharacterEncoding() :
                        "UTF-8";
                jsonObject.put("payload",new String(requestWrapper.toByteArray(), charEncoding));
            } catch (UnsupportedEncodingException e) {
                jsonObject.put("payload","Failed to parse request payload");
            }

        }
        log.info(System.getProperty("line.separator")+JSONObject.toJSONString(jsonObject, SerializerFeature.PrettyFormat));
    }

    private void logResponse(final ResponseWrapper response) {
        JSONObject jsonObject=new JSONObject();
        jsonObject.put("response id",(response.getId()));
        try {
            jsonObject.put("payload",new String(response.toByteArray(), response.getCharacterEncoding()));
        } catch (Exception e) {
           jsonObject.put("payload","Failed to parse response payload");
        }
        log.info(System.getProperty("line.separator")+JSONObject.toJSONString(jsonObject, SerializerFeature.PrettyFormat));
    }
    private boolean isBinaryContent(final HttpServletRequest request) {
        if (request.getContentType() == null) {
            return false;
        }
        return request.getContentType().startsWith("image")
                || request.getContentType().startsWith("video")
                || request.getContentType().startsWith("audio");
    }

    private boolean isMultipart(final HttpServletRequest request) {
        return request.getContentType() != null
                && MediaType.MULTIPART_FORM_DATA.includes(MediaType.parseMediaType(request.getContentType()));
    }

    @Override
    public int getOrder() {
        return order;
    }

    public void setOrder(int order) {
        this.order = order;
    }
}